init
This commit is contained in:
commit
5aa9b32c03
27 changed files with 4142 additions and 0 deletions
225
lib/Store.js
Normal file
225
lib/Store.js
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
import { useState, useEffect } from 'react'
|
||||
import { createClient } from '@supabase/supabase-js'
|
||||
|
||||
export const supabase = createClient(
|
||||
process.env.NEXT_PUBLIC_SUPABASE_URL,
|
||||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY
|
||||
)
|
||||
|
||||
/**
|
||||
* @param {number} channelId the currently selected Channel
|
||||
*/
|
||||
export const useStore = (props) => {
|
||||
const [channels, setChannels] = useState([])
|
||||
const [messages, setMessages] = useState([])
|
||||
const [users] = useState(new Map())
|
||||
const [newMessage, handleNewMessage] = useState(null)
|
||||
const [newChannel, handleNewChannel] = useState(null)
|
||||
const [newOrUpdatedUser, handleNewOrUpdatedUser] = useState(null)
|
||||
const [deletedChannel, handleDeletedChannel] = useState(null)
|
||||
const [deletedMessage, handleDeletedMessage] = useState(null)
|
||||
|
||||
// Load initial data and set up listeners
|
||||
useEffect(() => {
|
||||
// Get Channels
|
||||
fetchChannels(setChannels)
|
||||
// Listen for new and deleted messages
|
||||
const messageListener = supabase
|
||||
.channel('public:messages')
|
||||
.on('postgres_changes', { event: 'INSERT', schema: 'public', table: 'messages' }, (payload) =>
|
||||
handleNewMessage(payload.new)
|
||||
)
|
||||
.on('postgres_changes', { event: 'DELETE', schema: 'public', table: 'messages' }, (payload) =>
|
||||
handleDeletedMessage(payload.old)
|
||||
)
|
||||
.subscribe()
|
||||
// Listen for changes to our users
|
||||
const userListener = supabase
|
||||
.channel('public:users')
|
||||
.on('postgres_changes', { event: '*', schema: 'public', table: 'users' }, (payload) =>
|
||||
handleNewOrUpdatedUser(payload.new)
|
||||
)
|
||||
.subscribe()
|
||||
// Listen for new and deleted channels
|
||||
const channelListener = supabase
|
||||
.channel('public:channels')
|
||||
.on('postgres_changes', { event: 'INSERT', schema: 'public', table: 'channels' }, (payload) =>
|
||||
handleNewChannel(payload.new)
|
||||
)
|
||||
.on('postgres_changes', { event: 'DELETE', schema: 'public', table: 'channels' }, (payload) =>
|
||||
handleDeletedChannel(payload.old)
|
||||
)
|
||||
.subscribe()
|
||||
// Cleanup on unmount
|
||||
return () => {
|
||||
supabase.removeChannel(supabase.channel(messageListener))
|
||||
supabase.removeChannel(supabase.channel(userListener))
|
||||
supabase.removeChannel(supabase.channel(channelListener))
|
||||
}
|
||||
}, [])
|
||||
|
||||
// Update when the route changes
|
||||
useEffect(() => {
|
||||
if (props?.channelId > 0) {
|
||||
fetchMessages(props.channelId, (messages) => {
|
||||
messages.forEach((x) => users.set(x.user_id, x.author))
|
||||
setMessages(messages)
|
||||
})
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [props.channelId])
|
||||
|
||||
// New message received from Postgres
|
||||
useEffect(() => {
|
||||
if (newMessage && newMessage.channel_id === Number(props.channelId)) {
|
||||
const handleAsync = async () => {
|
||||
let authorId = newMessage.user_id
|
||||
if (!users.get(authorId)) await fetchUser(authorId, (user) => handleNewOrUpdatedUser(user))
|
||||
setMessages(messages.concat(newMessage))
|
||||
}
|
||||
handleAsync()
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [newMessage])
|
||||
|
||||
// Deleted message received from postgres
|
||||
useEffect(() => {
|
||||
if (deletedMessage) setMessages(messages.filter((message) => message.id !== deletedMessage.id))
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [deletedMessage])
|
||||
|
||||
// New channel received from Postgres
|
||||
useEffect(() => {
|
||||
if (newChannel) setChannels(channels.concat(newChannel))
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [newChannel])
|
||||
|
||||
// Deleted channel received from postgres
|
||||
useEffect(() => {
|
||||
if (deletedChannel) setChannels(channels.filter((channel) => channel.id !== deletedChannel.id))
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [deletedChannel])
|
||||
|
||||
// New or updated user received from Postgres
|
||||
useEffect(() => {
|
||||
if (newOrUpdatedUser) users.set(newOrUpdatedUser.id, newOrUpdatedUser)
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [newOrUpdatedUser])
|
||||
|
||||
return {
|
||||
// We can export computed values here to map the authors to each message
|
||||
messages: messages.map((x) => ({ ...x, author: users.get(x.user_id) })),
|
||||
channels: channels !== null ? channels.sort((a, b) => a.slug.localeCompare(b.slug)) : [],
|
||||
users,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all channels
|
||||
* @param {function} setState Optionally pass in a hook or callback to set the state
|
||||
*/
|
||||
export const fetchChannels = async (setState) => {
|
||||
try {
|
||||
let { data } = await supabase.from('channels').select('*')
|
||||
if (setState) setState(data)
|
||||
return data
|
||||
} catch (error) {
|
||||
console.log('error', error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a single user
|
||||
* @param {number} userId
|
||||
* @param {function} setState Optionally pass in a hook or callback to set the state
|
||||
*/
|
||||
export const fetchUser = async (userId, setState) => {
|
||||
try {
|
||||
let { data } = await supabase.from('users').select(`*`).eq('id', userId)
|
||||
let user = data[0]
|
||||
if (setState) setState(user)
|
||||
return user
|
||||
} catch (error) {
|
||||
console.log('error', error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all messages and their authors
|
||||
* @param {number} channelId
|
||||
* @param {function} setState Optionally pass in a hook or callback to set the state
|
||||
*/
|
||||
export const fetchMessages = async (channelId, setState) => {
|
||||
try {
|
||||
let { data } = await supabase
|
||||
.from('messages')
|
||||
.select(`*, author:user_id(*)`)
|
||||
.eq('channel_id', channelId)
|
||||
.order('inserted_at', { ascending: true })
|
||||
if (setState) setState(data)
|
||||
return data
|
||||
} catch (error) {
|
||||
console.log('error', error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new channel into the DB
|
||||
* @param {string} slug The channel name
|
||||
* @param {number} user_id The channel creator
|
||||
*/
|
||||
export const addChannel = async (slug, user_id) => {
|
||||
try {
|
||||
let { data } = await supabase
|
||||
.from('channels')
|
||||
.insert([{ slug, created_by: user_id }])
|
||||
.select()
|
||||
return data
|
||||
} catch (error) {
|
||||
console.log('error', error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new message into the DB
|
||||
* @param {string} message The message text
|
||||
* @param {number} channel_id
|
||||
* @param {number} user_id The author
|
||||
*/
|
||||
export const addMessage = async (message, channel_id, user_id) => {
|
||||
try {
|
||||
let { data } = await supabase
|
||||
.from('messages')
|
||||
.insert([{ message, channel_id, user_id }])
|
||||
.select()
|
||||
return data
|
||||
} catch (error) {
|
||||
console.log('error', error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a channel from the DB
|
||||
* @param {number} channel_id
|
||||
*/
|
||||
export const deleteChannel = async (channel_id) => {
|
||||
try {
|
||||
let { data } = await supabase.from('channels').delete().match({ id: channel_id })
|
||||
return data
|
||||
} catch (error) {
|
||||
console.log('error', error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a message from the DB
|
||||
* @param {number} message_id
|
||||
*/
|
||||
export const deleteMessage = async (message_id) => {
|
||||
try {
|
||||
let { data } = await supabase.from('messages').delete().match({ id: message_id })
|
||||
return data
|
||||
} catch (error) {
|
||||
console.log('error', error)
|
||||
}
|
||||
}
|
||||
5
lib/UserContext.js
Normal file
5
lib/UserContext.js
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
import { createContext } from 'react'
|
||||
|
||||
const UserContext = createContext()
|
||||
|
||||
export default UserContext
|
||||
Loading…
Add table
Add a link
Reference in a new issue