import { useState, useEffect } from 'react' import { createClient } from '@supabase/supabase-js' const supabase = createClient(process.env.SUPABASE_URL, process.env.SUPABASE_KEY) /** * @param {number} channelId the currently selected Channel */ export const useStore = props => { const [channels, setChannels] = useState(new Array()) const [messages, setMessages] = useState(new Array()) const [users] = useState(new Map()) const [newMessage, handleNewMessage] = useState(null) const [newChannel, handleNewChannel] = useState(null) const [newOrUpdatedUser, handleNewOrUpdatedUser] = useState(null) // Load initial data and set up listeners useEffect(() => { // Get Channels fetchChannels(setChannels) // Listen for new messages const messageListener = supabase .from('messages') .on('INSERT', payload => handleNewMessage(payload.new)) .subscribe() // Listen for changes to our users const userListener = supabase .from('users') .on('*', payload => handleNewOrUpdatedUser(payload.new)) .subscribe() // Listen for new channels const channelListener = supabase .from('channels') .on('INSERT', payload => handleNewChannel(payload.new)) .subscribe() // Cleanup on unmount return () => { messageListener.unsubscribe() userListener.unsubscribe() channelListener.unsubscribe() } }, []) // 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) }) } }, [props.channelId]) // New message recieved from Postgres useEffect(() => { if (newMessage && newMessage.channel_id == 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() } }, [newMessage]) // New channel recieved from Postgres useEffect(() => { if (newChannel) setChannels(channels.concat(newChannel)) }, [newChannel]) // New or updated user recieved from Postgres useEffect(() => { if (newOrUpdatedUser) users.set(newOrUpdatedUser.id, newOrUpdatedUser) }, [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.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 { body } = await supabase.from('channels').select('*') if (setState) setState(body) return body } 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 { body } = await supabase .from('users') .eq('id', userId) .select(`*`) let user = body[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 { body } = await supabase .from('messages') .eq('channel_id', channelId) .select(`*, author:user_id(*)`) .order('inserted_at', true) if (setState) setState(body) return body } catch (error) { console.log('error', error) } } /** * Insert a new channel into the DB * @param {string} slug The channel name */ export const addChannel = async slug => { try { let { body } = await supabase.from('channels').insert([{ slug }]) return body } 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 { body } = await supabase.from('messages').insert([{ message, channel_id, user_id }]) return body } catch (error) { console.log('error', error) } }