Skip to content

Commit

Permalink
chore: add social network example
Browse files Browse the repository at this point in the history
  • Loading branch information
fmvilas authored Dec 23, 2021
1 parent f98f0db commit f478ceb
Show file tree
Hide file tree
Showing 38 changed files with 16,949 additions and 1 deletion.
3 changes: 2 additions & 1 deletion .eslintignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
node_modules
runtimes
runtimes
examples/social-network/frontend
2 changes: 2 additions & 0 deletions .sonarcloud.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#we need to explicitly exclude them as they're not relevant to the source code
sonar.exclusions=examples/**/*
19 changes: 19 additions & 0 deletions examples/social-network/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Social Network Example

This example was created as part of the [talk Fran Mendez did at QCon Plus 2021](https://plus.qconferences.com/plus2021/speakers/fran-mendez).

Here's an overview of what this example is about:

![](./architecture.jpg)

## Run it

There are three services. All of them can be started by running:

```
npm run dev
```

You'll have to customize the `db.json` file with your own data (especially the Slack IDs).

For the notifications service, you'll have to configure the Slack API URL in the `.env` file. Head over https://api.slack.com/messaging/webhooks to know more.
Binary file added examples/social-network/architecture.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
46 changes: 46 additions & 0 deletions examples/social-network/db.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"likes": [
],
"users": {
"1": {
"id": 1,
"slackId": "<@U34F2JRRS>",
"coverImageUrl": "https://pbs.twimg.com/profile_banners/87489271/1617659258/1500x500",
"imageUrl": "https://pbs.twimg.com/profile_images/1373387614238179328/cB1gp6Lh_400x400.jpg",
"name": "Fran Mendez"
},
"2": {
"id": 2,
"slackId": "<@UD698Q5LM>",
"coverImageUrl": "https://pbs.twimg.com/profile_banners/407861759/1632725036/1500x500",
"imageUrl": "https://pbs.twimg.com/profile_images/1435858354967093254/carWrDQa_400x400.jpg",
"name": "Lukasz Gornicki"
}
},
"posts": [
{
"id": 1,
"userId": 1,
"imageUrl": "https://images.unsplash.com/photo-1466637574441-749b8f19452f?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=880&q=80",
"text": "Cooking at home with some friends and good wine 🍷"
},
{
"id": 11,
"userId": 2,
"imageUrl": "https://images.unsplash.com/photo-1502599213010-875782f9bf52?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=697&q=80",
"text": "The best of working remotely? Hacking on my favorite coffee-shop 🥤🤙🏽<br /><br /><strong>#lifestyle</strong>"
},
{
"id": 2,
"userId": 1,
"imageUrl": "https://images.unsplash.com/photo-1503919545889-aef636e10ad4?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=687&q=80",
"text": "Going to the forest 🌳🦌 with my little homie 👧🏻"
},
{
"id": 10,
"userId": 2,
"imageUrl": "https://images.unsplash.com/photo-1586348943529-beaae6c28db9?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=512&q=80",
"text": "I was missing a bit of this. Holidays, finally!"
}
]
}
34 changes: 34 additions & 0 deletions examples/social-network/frontend/.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
29 changes: 29 additions & 0 deletions examples/social-network/frontend/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Next.js + Tailwind CSS Example

This example shows how to use [Tailwind CSS](https://tailwindcss.com/) [(v2.2)](https://blog.tailwindcss.com/tailwindcss-2-2) with Next.js. It follows the steps outlined in the official [Tailwind docs](https://tailwindcss.com/docs/guides/nextjs).

It uses the new [`Just-in-Time Mode`](https://tailwindcss.com/docs/just-in-time-mode) for Tailwind CSS.

## Preview

Preview the example live on [StackBlitz](http://stackblitz.com/):

[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/vercel/next.js/tree/canary/examples/with-tailwindcss)

## Deploy your own

Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example):

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https://github.com/vercel/next.js/tree/canary/examples/with-tailwindcss&project-name=with-tailwindcss&repository-name=with-tailwindcss)

## How to use

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 with-tailwindcss with-tailwindcss-app
# or
yarn create next-app --example with-tailwindcss with-tailwindcss-app
```

Deploy it to the cloud with [Vercel](https://vercel.com/new?utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)).
9 changes: 9 additions & 0 deletions examples/social-network/frontend/components/Avatar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export default function Avatar({ imageUrl, name, className='' }) {
return (
<img
className={`inline-block h-10 w-10 rounded-full ${className}`}
src={imageUrl}
alt={name}
/>
)
}
37 changes: 37 additions & 0 deletions examples/social-network/frontend/components/Header.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { MailIcon } from '@heroicons/react/solid'
import classNames from 'classnames'

export default function Header({profile}) {
return (
<div>
<div>
<img className="h-32 w-full object-cover lg:h-48" src={profile.coverImageUrl} alt="" />
</div>
<div className="max-w-5xl mx-auto px-4 sm:px-6 lg:pl-8 lg:pr-4">
<div className="-mt-12 sm:-mt-16 sm:flex sm:items-end sm:space-x-5">
<div className="flex">
<img
className="h-24 w-24 rounded-full ring-4 ring-white sm:h-32 sm:w-32"
src={profile.imageUrl}
alt=""
/>
</div>
<div className="mt-6 sm:flex-1 sm:min-w-0 sm:flex sm:items-center sm:justify-end sm:space-x-6 sm:pb-1">
<div className="sm:block mt-6 min-w-0 flex-1">
<h1 className="text-2xl font-bold text-gray-900 truncate">{profile.name}</h1>
</div>
<div className="mt-6 flex flex-col justify-stretch space-y-3 sm:flex-row sm:space-y-0 sm:space-x-4">
<button
type="button"
className="inline-flex justify-center px-4 py-2 border border-gray-300 shadow-sm text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-pink-500"
>
<MailIcon className="-ml-1 mr-2 h-5 w-5 text-gray-400" aria-hidden="true" />
<span>Message</span>
</button>
</div>
</div>
</div>
</div>
</div>
)
}
59 changes: 59 additions & 0 deletions examples/social-network/frontend/components/Post.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { useContext, useEffect, useState } from 'react'
import { HeartIcon } from '@heroicons/react/outline'
import { HeartIcon as HeartIconSolid } from '@heroicons/react/solid'
import WS from '../helpers/ws'
import UserContext from '../context/user'
import Avatar from './Avatar'

export default function Post({ post, user }) {
const loggedInUser = useContext(UserContext)
const [liked, setLiked] = useState(!!post.likes.find(l => l.postId === post.id && l.userId === loggedInUser.id))
const [likeCount, setLikes] = useState(post.likes.filter(l => l.postId === post.id).length)

const onLike = () => {
if (liked) {
WS.send('dislike', { postId: post.id, userId: loggedInUser.id })
setLiked(false)
} else {
WS.send('like', { postId: post.id, userId: loggedInUser.id })
setLiked(true)
}
}

useEffect(() => {
if (typeof window !== 'undefined') {
WS.listen('likes_count_updated', (data) => {
if (data.postId === post.id) {
setLikes(data.totalCount)
}
})
}
}, [])

return (
<div className="mb-16">
<div className="mb-4">
<Avatar imageUrl={user.imageUrl} name={user.name} className="mr-4" />
<strong>{user.name}</strong>
</div>
<p className="text-gray-700 my-2" dangerouslySetInnerHTML={{__html: post.text}} />
<img className="w-full object-cover max-h-96" src={post.imageUrl} alt="" />
<div className="mt-2">
<button
type="button"
className={`inline-flex items-center px-3 py-2 text-md font-medium ${liked ? 'text-red-600' : 'text-gray-700'} bg-white hover:text-red-600 focus:outline-none`}
onClick={onLike}
>
{
liked ? (
<HeartIconSolid className="-ml-0.5 mr-2 h-4 w-4" aria-hidden="true" />
) : (
<HeartIcon className="-ml-0.5 mr-2 h-4 w-4" aria-hidden="true" />
)
}
{likeCount}
</button>
</div>
</div>
)
}
3 changes: 3 additions & 0 deletions examples/social-network/frontend/context/user.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { createContext } from 'react'

export default createContext()
34 changes: 34 additions & 0 deletions examples/social-network/frontend/helpers/ws.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
export default class WS {
static ws
static listeners = {}

static init() {
this.ws = new WebSocket('ws://localhost:3001/')

this.ws.addEventListener('open', () => {
console.log('WS client connected to server!')
})

this.ws.addEventListener('message', (event) => {
try {
const json = JSON.parse(event.data)
console.log(json)
if (this.listeners[json.type]) {
this.listeners[json.type].forEach(listener => listener(json.data))
}
} catch (e) {
console.error('Unable to decode the WebSocket message:')
console.error(e)
}
})
}

static listen(eventName, fn) {
this.listeners[eventName] = this.listeners[eventName] || []
this.listeners[eventName].push(fn)
}

static send(eventName, payload) {
this.ws.send(JSON.stringify({ type: eventName, data: payload }))
}
}
Loading

0 comments on commit f478ceb

Please sign in to comment.