Skip to content

Commit

Permalink
Example blog with comments (#24829)
Browse files Browse the repository at this point in the history
* initial commit

* delete comment

* env name fix

* Update README.md

* remove hapi-boom

* use next-image

* fix alt attr

* date fix for blog posts

* reset gitignore

* fix react best-practices

* prettier

* mdx to md

* fix prettier config. lint 👍

* Update examples/blog-with-comment/components/comment/list.js

Co-authored-by: Lee Robinson <me@leerob.io>

* refactor api methods

* fix: blog title

* fix: html lang

* next-mdx to gray-matter

Co-authored-by: Noah Fischer <78238464+noahfschr@users.noreply.github.com>
Co-authored-by: Lee Robinson <me@leerob.io>
Co-authored-by: Enes Akar <enesakar@gmail.com>
  • Loading branch information
4 people authored Jun 11, 2021
1 parent 8227973 commit 5142c0e
Show file tree
Hide file tree
Showing 32 changed files with 842 additions and 0 deletions.
4 changes: 4 additions & 0 deletions examples/blog-with-comment/.env.local.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
REDIS_URL=
NEXT_PUBLIC_AUTH0_CLIENT_ID=
NEXT_PUBLIC_AUTH0_DOMAIN=
NEXT_PUBLIC_AUTH0_ADMIN_EMAIL=
34 changes: 34 additions & 0 deletions examples/blog-with-comment/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

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

# vercel
.vercel
4 changes: 4 additions & 0 deletions examples/blog-with-comment/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"singleQuote": true,
"semi": false
}
63 changes: 63 additions & 0 deletions examples/blog-with-comment/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Blog with Comment

This project adds commenting functionality to [Next.js blog application](https://github.com/vercel/next.js/tree/canary/examples/blog) using Upstash and Auth0.

The comment box requires Auth0 authentication for users to add new comments. A user can delete their own comment. Also admin user can delete any comment.

Comments are stored in Serverless Redis ([Upstash](http://upstash.com/)).

### Demo

[https://blog-with-comment.vercel.app/](https://blog-with-comment.vercel.app/)

## `1` Project set up

Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app)
with [npm](https://docs.npmjs.com/cli/init) or [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) to bootstrap the
example:

```bash
npx create-next-app --example blog-with-comment blog-with-comment-app
```

## `2` Set up environment variables

Copy the `.env.local.example` file in this directory to `.env.local` (which will be ignored by Git):

```bash
cp .env.local.example .env.local
```

## `3` Configuring Upstash

Go to the [Upstash Console](https://console.upstash.com/) and create a new database

#### Upstash environment

- `REDIS_URL`: Find the URL in the database details page in Upstash Console clicking on **Redis Connect** button.

## `4` Configuring Auth0

1. Go to the [Auth0 dashboard](https://manage.auth0.com/) and create a new application of type **Single Page Web
Applications**.
2. Go to the settings page of the application
3. Configure the following settings:
- **Allowed Callback URLs**: Should be set to `http://localhost:3000/` when testing locally or typically
to `https://myapp.com/` when deploying your application.
- **Allowed Logout URLs**: Should be set to `http://localhost:3000/` when testing locally or typically
to `https://myapp.com/` when deploying your application.
4. Save the settings.

#### Auth0 environment

- `NEXT_PUBLIC_AUTH0_DOMAIN`: Can be found in the Auth0 dashboard under `settings`.
- `NEXT_PUBLIC_AUTH0_CLIENT_ID`: Can be found in the Auth0 dashboard under `settings`.
- `NEXT_PUBLIC_AUTH0_ADMIN_EMAIL`: This is the email of the admin user which you use while singing in Auth0. Admin is able to delete any comment.

## Deploy Your Local Project

To deploy your local project to Vercel, push it to GitHub/GitLab/Bitbucket
and [import to Vercel](https://vercel.com/new?utm_source=github&utm_medium=readme&utm_campaign=upstash-roadmap).

**Important**: When you import your project on Vercel, make sure to click on **Environment Variables** and set them to
match your `.env.local` file.
18 changes: 18 additions & 0 deletions examples/blog-with-comment/_posts/long-expected-party.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
title: "A Long-expected Party"
excerpt: "Gandalf arrives in the Shire for Bilbo's Farewell Birthday Party.
Bilbo leaves the Shire permanently."
date: "2021-03-02"
---

Placeat consequuntur ullam aut sapiente illo velit. Eius facere ut molestias
totam laborum pariatur quam. Praesentium quo veritatis expedita animi.

Quite anything glass benefit. Such form clearly top tend can require my. Federal
degree sort performance region maintain.

Ut dignissimos sapiente culpa rerum pariatur consequatur. Corporis suscipit ad
corrupti aut. Expedita culpa aut deleniti officiis.

Porro eum id sit quia expedita. Alias expedita asperiores. Corporis ex eum atque
cum ea.
18 changes: 18 additions & 0 deletions examples/blog-with-comment/_posts/passing-of-grey-company.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
title: "The Passing of the Grey Company"
excerpt: "Aragorn, Legolas, and Gimli are accompanied by the Grey Company as
they pass through the Paths of the Dead between Rohan and Gondor."
date: "2021-01-22"
---

Placeat consequuntur ullam aut sapiente illo velit. Eius facere ut molestias
totam laborum pariatur quam. Praesentium quo veritatis expedita animi.

Quite anything glass benefit. Such form clearly top tend can require my. Federal
degree sort performance region maintain.

Ut dignissimos sapiente culpa rerum pariatur consequatur. Corporis suscipit ad
corrupti aut. Expedita culpa aut deleniti officiis.

Porro eum id sit quia expedita. Alias expedita asperiores. Corporis ex eum atque
cum ea.
18 changes: 18 additions & 0 deletions examples/blog-with-comment/_posts/prancing-pony.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
title: "At the Sign of the Prancing Pony"
excerpt: "The Hobbits reach the The Prancing Pony inn at Bree, where Frodo uses
a false name, Underhill."
date: "2021-03-03"
---

Placeat consequuntur ullam aut sapiente illo velit. Eius facere ut molestias
totam laborum pariatur quam. Praesentium quo veritatis expedita animi.

Quite anything glass benefit. Such form clearly top tend can require my. Federal
degree sort performance region maintain.

Ut dignissimos sapiente culpa rerum pariatur consequatur. Corporis suscipit ad
corrupti aut. Expedita culpa aut deleniti officiis.

Porro eum id sit quia expedita. Alias expedita asperiores. Corporis ex eum atque
cum ea.
18 changes: 18 additions & 0 deletions examples/blog-with-comment/_posts/riders-of-rohan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
title: "The Riders of Rohan"
excerpt: "Aragorn, Legolas, and Gimli follow the trail of the Orcs and find
several clues as to what happened with Merry and Pippin."
date: "2021-02-01"
---

Placeat consequuntur ullam aut sapiente illo velit. Eius facere ut molestias
totam laborum pariatur quam. Praesentium quo veritatis expedita animi.

Quite anything glass benefit. Such form clearly top tend can require my. Federal
degree sort performance region maintain.

Ut dignissimos sapiente culpa rerum pariatur consequatur. Corporis suscipit ad
corrupti aut. Expedita culpa aut deleniti officiis.

Porro eum id sit quia expedita. Alias expedita asperiores. Corporis ex eum atque
cum ea.
45 changes: 45 additions & 0 deletions examples/blog-with-comment/components/comment/form.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { useAuth0 } from '@auth0/auth0-react'

function CommentForm({ text, setText, onSubmit }) {
const { isAuthenticated, logout, loginWithPopup } = useAuth0()

return (
<form onSubmit={onSubmit}>
<textarea
className="flex w-full max-h-40 p-3 rounded resize-y bg-gray-200 text-gray-900 placeholder-gray-500"
rows="2"
placeholder={
isAuthenticated
? `What are your thoughts?`
: 'Please login to leave a comment'
}
onChange={(e) => setText(e.target.value)}
value={text}
disabled={!isAuthenticated}
/>

<div className="flex items-center mt-4">
{isAuthenticated ? (
<div className="flex items-center space-x-6">
<button className="py-2 px-4 rounded bg-blue-600 text-white disabled:opacity-40 hover:bg-blue-700">
Send
</button>
<button className="text-gray-500" onClick={() => logout()}>
Log out
</button>
</div>
) : (
<button
type="button"
className="py-2 px-4 rounded bg-blue-600 text-white disabled:opacity-40 hover:bg-blue-700"
onClick={() => loginWithPopup()}
>
Log In
</button>
)}
</div>
</form>
)
}

export default CommentForm
16 changes: 16 additions & 0 deletions examples/blog-with-comment/components/comment/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import CommentForm from './form'
import CommentList from './list'
import useComments from '../../hooks/useComment'

function Comment() {
const { text, setText, comments, onSubmit, onDelete } = useComments()

return (
<div className="mt-20">
<CommentForm onSubmit={onSubmit} text={text} setText={setText} />
<CommentList comments={comments} onDelete={onDelete} />
</div>
)
}

export default Comment
52 changes: 52 additions & 0 deletions examples/blog-with-comment/components/comment/list.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import distanceToNow from '../../lib/dateRelative'
import { useAuth0 } from '@auth0/auth0-react'

function CommentList({ comments, onDelete }) {
const { user } = useAuth0()

return (
<div className="space-y-6 mt-10">
{comments.map((comment) => {
const isAuthor = user && user.sub === comment.user.sub
const isAdmin =
user && user.email === process.env.NEXT_PUBLIC_AUTH0_ADMIN_EMAIL

return (
<div key={comment.created_at} className="flex space-x-4">
<div className="flex-shrink-0">
<img
src={comment.user.picture}
alt={comment.user.name}
width={40}
height={40}
className="rounded-full"
/>
</div>

<div className="flex-grow">
<div className="flex space-x-2">
<b>{comment.user.name}</b>
<time className="text-gray-400">
{distanceToNow(comment.created_at)}
</time>
{(isAdmin || isAuthor) && (
<button
className="text-gray-400 hover:text-red-500"
onClick={() => onDelete(comment)}
aria-label="Close"
>
x
</button>
)}
</div>

<div>{comment.text}</div>
</div>
</div>
)
})}
</div>
)
}

export default CommentList
5 changes: 5 additions & 0 deletions examples/blog-with-comment/components/container.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
function Container({ children }) {
return <div className="container max-w-2xl m-auto px-4">{children}</div>
}

export default Container
21 changes: 21 additions & 0 deletions examples/blog-with-comment/components/header.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import Link from 'next/link'
import Container from '../components/container'

function Header() {
return (
<header className="py-6">
<Container>
<nav className="flex space-x-4">
<Link href="/">
<a>About</a>
</Link>
<Link href="/posts">
<a>Posts</a>
</Link>
</nav>
</Container>
</header>
)
}

export default Header
64 changes: 64 additions & 0 deletions examples/blog-with-comment/hooks/useComment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { useState, useEffect } from 'react'
import useSWR from 'swr'
import { useAuth0 } from '@auth0/auth0-react'

export default function useComments() {
const { getAccessTokenSilently } = useAuth0()
const [text, setText] = useState('')
const [url, setUrl] = useState(null)

const { data: comments, mutate } = useSWR(
() => {
const query = new URLSearchParams({ url })
return `/api/comment?${query.toString()}`
},
{
initialData: [],
}
)

useEffect(() => {
const url = window.location.origin + window.location.pathname
setUrl(url)
}, [])

const onSubmit = async (e) => {
e.preventDefault()
const token = await getAccessTokenSilently()

try {
await fetch('/api/comment', {
method: 'POST',
body: JSON.stringify({ url, text }),
headers: {
Authorization: token,
'Content-Type': 'application/json',
},
})
setText('')
await mutate()
} catch (err) {
console.log(err)
}
}

const onDelete = async (comment) => {
const token = await getAccessTokenSilently()

try {
await fetch('/api/comment', {
method: 'DELETE',
body: JSON.stringify({ url, comment }),
headers: {
Authorization: token,
'Content-Type': 'application/json',
},
})
await mutate()
} catch (err) {
console.log(err)
}
}

return { text, setText, comments, onSubmit, onDelete }
}
Loading

0 comments on commit 5142c0e

Please sign in to comment.