diff --git a/examples/slack-clone-basic-tutorial/stage-5-send-message/components/Layout.js b/examples/slack-clone-basic-tutorial/stage-5-realtime/components/Layout.js similarity index 100% rename from examples/slack-clone-basic-tutorial/stage-5-send-message/components/Layout.js rename to examples/slack-clone-basic-tutorial/stage-5-realtime/components/Layout.js diff --git a/examples/slack-clone-basic-tutorial/stage-5-send-message/components/Message.js b/examples/slack-clone-basic-tutorial/stage-5-realtime/components/Message.js similarity index 100% rename from examples/slack-clone-basic-tutorial/stage-5-send-message/components/Message.js rename to examples/slack-clone-basic-tutorial/stage-5-realtime/components/Message.js diff --git a/examples/slack-clone-basic-tutorial/stage-5-send-message/components/MessageInput.js b/examples/slack-clone-basic-tutorial/stage-5-realtime/components/MessageInput.js similarity index 100% rename from examples/slack-clone-basic-tutorial/stage-5-send-message/components/MessageInput.js rename to examples/slack-clone-basic-tutorial/stage-5-realtime/components/MessageInput.js diff --git a/examples/slack-clone-basic-tutorial/stage-5-send-message/db/schema.sql b/examples/slack-clone-basic-tutorial/stage-5-realtime/db/schema.sql similarity index 100% rename from examples/slack-clone-basic-tutorial/stage-5-send-message/db/schema.sql rename to examples/slack-clone-basic-tutorial/stage-5-realtime/db/schema.sql diff --git a/examples/slack-clone-basic-tutorial/stage-5-send-message/docker-compose.yml b/examples/slack-clone-basic-tutorial/stage-5-realtime/docker-compose.yml similarity index 100% rename from examples/slack-clone-basic-tutorial/stage-5-send-message/docker-compose.yml rename to examples/slack-clone-basic-tutorial/stage-5-realtime/docker-compose.yml diff --git a/examples/slack-clone-basic-tutorial/stage-5-send-message/lib/Store.js b/examples/slack-clone-basic-tutorial/stage-5-realtime/lib/Store.js similarity index 100% rename from examples/slack-clone-basic-tutorial/stage-5-send-message/lib/Store.js rename to examples/slack-clone-basic-tutorial/stage-5-realtime/lib/Store.js diff --git a/examples/slack-clone-basic-tutorial/stage-5-send-message/lib/UserContext.js b/examples/slack-clone-basic-tutorial/stage-5-realtime/lib/UserContext.js similarity index 100% rename from examples/slack-clone-basic-tutorial/stage-5-send-message/lib/UserContext.js rename to examples/slack-clone-basic-tutorial/stage-5-realtime/lib/UserContext.js diff --git a/examples/slack-clone-basic-tutorial/stage-5-send-message/next.config.js b/examples/slack-clone-basic-tutorial/stage-5-realtime/next.config.js similarity index 100% rename from examples/slack-clone-basic-tutorial/stage-5-send-message/next.config.js rename to examples/slack-clone-basic-tutorial/stage-5-realtime/next.config.js diff --git a/examples/slack-clone-basic-tutorial/stage-5-send-message/package-lock.json b/examples/slack-clone-basic-tutorial/stage-5-realtime/package-lock.json similarity index 100% rename from examples/slack-clone-basic-tutorial/stage-5-send-message/package-lock.json rename to examples/slack-clone-basic-tutorial/stage-5-realtime/package-lock.json diff --git a/examples/slack-clone-basic-tutorial/stage-5-send-message/package.json b/examples/slack-clone-basic-tutorial/stage-5-realtime/package.json similarity index 100% rename from examples/slack-clone-basic-tutorial/stage-5-send-message/package.json rename to examples/slack-clone-basic-tutorial/stage-5-realtime/package.json diff --git a/examples/slack-clone-basic-tutorial/stage-5-send-message/pages/[channelId].js b/examples/slack-clone-basic-tutorial/stage-5-realtime/pages/[channelId].js similarity index 100% rename from examples/slack-clone-basic-tutorial/stage-5-send-message/pages/[channelId].js rename to examples/slack-clone-basic-tutorial/stage-5-realtime/pages/[channelId].js diff --git a/examples/slack-clone-basic-tutorial/stage-5-send-message/pages/index.js b/examples/slack-clone-basic-tutorial/stage-5-realtime/pages/index.js similarity index 100% rename from examples/slack-clone-basic-tutorial/stage-5-send-message/pages/index.js rename to examples/slack-clone-basic-tutorial/stage-5-realtime/pages/index.js diff --git a/examples/slack-clone-basic-tutorial/stage-5-send-message/styles/style.scss b/examples/slack-clone-basic-tutorial/stage-5-realtime/styles/style.scss similarity index 100% rename from examples/slack-clone-basic-tutorial/stage-5-send-message/styles/style.scss rename to examples/slack-clone-basic-tutorial/stage-5-realtime/styles/style.scss diff --git a/examples/slack-clone-basic/components/MessageInput.js b/examples/slack-clone-basic/components/MessageInput.js index cbc81b71185..864f3a80042 100644 --- a/examples/slack-clone-basic/components/MessageInput.js +++ b/examples/slack-clone-basic/components/MessageInput.js @@ -1,4 +1,4 @@ -import { useState, useEffect } from 'react' +import { useState } from 'react' const MessageInput = ({onSubmit}) => { const [messageText, setMessageText] = useState('') diff --git a/web/blog/basic-slack-clone-react-hooks.mdx b/web/blog/basic-slack-clone-react-hooks.mdx index e868b33e462..b44e80d701b 100644 --- a/web/blog/basic-slack-clone-react-hooks.mdx +++ b/web/blog/basic-slack-clone-react-hooks.mdx @@ -105,10 +105,10 @@ language="bash" ### Setup all the files ```bash -mkdir components && touch components/{Layout.js,Message.js,MessageInput.js} && \ +mkdir components && touch components/MessageInput.js && \ mkdir db && touch db/schema.sql && \ -mkdir lib && touch lib/{Store.js,UserContext.js} && \ touch pages/{[channelId].js} && \ +touch Store.js && \ touch docker-compose.yml ``` @@ -116,20 +116,16 @@ You will now have the following boilerplate, ready to fill in with code. ```bash ├── components -│   ├── Layout.js # Page Layout -│   ├── Message.js # Individual Messages │   └── MessageInput.js # The text input for writing new messages ├── db │   └── schema.sql # Basic schema and dummy data -├── lib -│   ├── Store.js # A basic data store using React Hooks -│   └── UserContext.js # Passes the logged in user to the App -├── next.config.js ├── pages │   ├── [channelId].js # Page for showing all the messages in a channel -│   └── index.js # Login page +│   └── index.js # Home page ├── styles │   └── style.scss # SASS file for styling our app +├── next.config.js # For passing ENV variables to our app +├── Store.js # A basic data store using React Hooks └── docker-compose.yml # Docker compose for Postgres and Supabase ``` @@ -342,18 +338,115 @@ import '../styles/style.scss' -## Stage 5: Making it look like Slack +## Stage 5: Adding realtime functionality + +Make the following changes to the store. -
-All the code for this section can be found in ... -
+ +### Handle new messages + +```js +const [newMessage, handleNewMessage] = useState(null) + +// New message recieved from Postgres +useEffect(() => { + if (newMessage) { + let author = users.get(newMessage.user_id) + setMessages(messages.concat({...newMessage, author })) + } +}, [newMessage]) + +``` + +### Lisen to the database + +```js {5-16} +useEffect(() => { + fetchChannels(setChannels) + fetchMessages(setMessages) + + // Listen for new messages + const messageListener = supabase + .from('messages') + .eq('channel_id', channelId) + .on('*', payload => handleNewMessage(payload.new)) + .subscribe() -Tailwind + return () => { + // Cleanup on unmount + messageListener.unsubscribe() + } +}) +``` + +## Stage 6: Sending new messages + +For now we will assume that we are user 2 (tonystark). + +### Add to store + + +```js +export const addMessage = async (message, channel_id, user_id) => { + try { + let { body } = await supabase.from('messages').insert([{ message, channel_id, user_id }]) + return body + } catch (error) { + console.log('error', error) + } +} +``` + +### Add message component + + +Create component + +```js +import { useState } from 'react' +import { addMessage } from '~/lib/Store' + +const MessageInput = ({ channelId }) => { + const [messageText, setMessageText] = useState('') + + const submitOnEnter = event => { + // Watch for enter key + if (event.keyCode === 13) { + addMessage(messageText, channelId, 2) + setMessageText('') + } + } + + return ( + <> + setMessageText(e.target.value)} + onKeyDown={e => submitOnEnter(e)} + /> + + )} + +export default MessageInput +``` + +Add to Channel Page + +```js +// @todo + +``` ## Wrapping up -You now have a realtime application. The combination of Docker + Postgres + Supabase made it quick and easy to get going on our local machines so that we could spend most of the time building the product. -If you liked this tutorial, make sure to sign up for [Supabase](http://supabase.io) as we continue to bring all the functionality of Firebase to Postgres. +- You now have a realtime application. Open on multiple browsers to see realtime. +- The combination of Docker + Postgres + Supabase made it quick and easy to get going on our local machines so that we could spend most of the time building the product. +- Link to the example with user auth, adding channels + +If you liked this tutorial, make sure to sign up for [Supabase](http://supabase.io) as we continue to bring all the functionality of Firebase to Postgres. diff --git a/web/blog/basic-slack-clone-react-hooks/snippets.js b/web/blog/basic-slack-clone-react-hooks/snippets.js index e553490e9cd..feabcadb291 100644 --- a/web/blog/basic-slack-clone-react-hooks/snippets.js +++ b/web/blog/basic-slack-clone-react-hooks/snippets.js @@ -15,14 +15,12 @@ import { createClient } from '@supabase/supabase-js' const supabase = createClient(process.env.SUPABASE_URL, process.env.SUPABASE_KEY) export const useStore = ({ channelId }) => { - const [channels, setChannels] = useState(new Array()) - const [messages, setMessages] = useState(new Array()) - const [users] = useState(new Map()) + const [channels, setChannels] = useState([]) + const [messages, setMessages] = useState([]) // Fetches messages for the channel and starts the channel listeners const reloadStore = () => { if (channelId) { - console.log('channelId', channelId) fetchChannels(setChannels) fetchMessages(channelId, setMessages) } @@ -31,14 +29,11 @@ export const useStore = ({ channelId }) => { // Update the store when the user changes the "channel" useEffect(reloadStore, [channelId]) - // Export computed properties to use in our app - return { - messages: messages.map(x => ({ ...x, author: users.get(x.user_id) })), - channels: channels.sort((a, b) => a.slug.localeCompare(b.slug)), - users, - } + // Export properties to use in our app + return { messages, channels, channelId } } +// Get all the channels export const fetchChannels = async callback => { try { let { body } = await supabase.from('channels').select('*') @@ -49,6 +44,7 @@ export const fetchChannels = async callback => { } } +// Get all the messages and the authors in a single request export const fetchMessages = async (channelId, callback) => { try { let { body } = await supabase