### Join Chat Room Button (Client-Side) - TypeScript Source: https://context7.com/webdevsimplified/supabase-realtime-chat/llms.txt A client-side component that provides a button to join a specific chat room. It uses the `useCurrentUser` hook to get the logged-in user and the Supabase client to insert a new entry into the `chat_room_member` table. Upon successful joining, it refreshes the router and navigates the user to the room's page. This component requires `roomId` as a prop and depends on `useCurrentUser` and the Supabase client. ```typescript // src/components/join-room-button.tsx "use client" import { ComponentProps } from "react" import { ActionButton } from "./ui/action-button" import { useCurrentUser } from "@/services/supabase/hooks/useCurrentUser" import { createClient } from "@/services/supabase/client" import { useRouter } from "next/navigation" export function JoinRoomButton({ children, roomId, ...props }: Omit, "action"> & { roomId: string }) { const { user } = useCurrentUser() const router = useRouter() async function joinRoom() { if (user == null) { return { error: true, message: "User not logged in" } } const supabase = createClient() const { error } = await supabase.from("chat_room_member").insert({ chat_room_id: roomId, member_id: user.id, }) if (error) { return { error: true, message: "Failed to join room" } } router.refresh() router.push(`/rooms/${roomId}`) return { error: false } } return ( {children} ) } // Usage Join Room ``` -------------------------------- ### Get Current User (Server-Side Cache) (Next.js/TS) Source: https://context7.com/webdevsimplified/supabase-realtime-chat/llms.txt A cached server-side function to securely retrieve the currently authenticated user's data from Supabase. This function is intended for use within Next.js server components to avoid redundant data fetching. It redirects to the login page if no user is found. ```typescript import { cache } from "react" import { createClient } from "../server" export const getCurrentUser = cache(async () => { const supabase = await createClient() return (await supabase.auth.getUser()).data.user }) // Usage in server components import { getCurrentUser } from "@/services/supabase/lib/getCurrentUser" import { redirect } from "next/navigation" export default async function Page() { const user = await getCurrentUser() if (user == null) { redirect("/auth/login") } // Use user.id, user.email, etc. return
Welcome {user.email}
} ``` -------------------------------- ### Use Current User Hook (Client-Side) - TypeScript Source: https://context7.com/webdevsimplified/supabase-realtime-chat/llms.txt A React hook to fetch and subscribe to the current authenticated user's real-time auth state changes. It initializes by getting the current user and then listens for any authentication state changes, updating the user state accordingly. This hook is essential for client components that need to display user-specific information or conditionally render content based on login status. ```typescript // src/services/supabase/hooks/useCurrentUser.ts import { useEffect, useState } from "react" import { createClient } from "../client" import { User } from "@supabase/supabase-js" export function useCurrentUser() { const [isLoading, setIsLoading] = useState(true) const [user, setUser] = useState(null) useEffect(() => { const supabase = createClient() supabase.auth .getUser() .then(({ data }) => { setUser(data.user) }) .finally(() => { setIsLoading(false) }) const { data } = supabase.auth.onAuthStateChange((_, session) => { setUser(session?.user ?? null) }) return () => { data.subscription.unsubscribe() } }, []) return { user, isLoading } } // Usage in client components "use client" import { useCurrentUser } from "@/services/supabase/hooks/useCurrentUser" export function MyComponent() { const { user, isLoading } = useCurrentUser() if (isLoading) return
Loading...
if (!user) return
Not logged in
return
Hello {user.email}
} ``` -------------------------------- ### Configure Supabase Client (TypeScript) Source: https://context7.com/webdevsimplified/supabase-realtime-chat/llms.txt Initializes Supabase clients for browser and server environments using the `@supabase/ssr` library. It leverages environment variables for Supabase URL and keys and incorporates TypeScript database types for enhanced safety. The server-side client also handles cookie management for Next.js server components. ```typescript // src/services/supabase/client.ts (Browser) import { createBrowserClient } from "@supabase/ssr" import { Database } from "./types/database" export function createClient() { return createBrowserClient( process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_OR_ANON_KEY! ) } // src/services/supabase/server.ts (Server) import { createServerClient } from "@supabase/ssr" import { cookies } from "next/headers" import { Database } from "./types/database" export async function createClient() { const cookieStore = await cookies() return createServerClient( process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_OR_ANON_KEY!, { cookies: { getAll() { return cookieStore.getAll() }, setAll(cookiesToSet) { try { cookiesToSet.forEach(({ name, value, options }) => cookieStore.set(name, value, options) ) } catch { // Ignore if called from Server Component } }, }, } ) } export function createAdminClient() { return createServerClient( process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.SUPABASE_SECRET_KEY!, { cookies: { getAll() { return [] }, }, } ) } // Usage // In client components import { createClient } from "@/services/supabase/client" const supabase = createClient() // In server components/actions import { createClient } from "@/services/supabase/server" const supabase = await createClient() // For admin operations (bypasses RLS) import { createAdminClient } from "@/services/supabase/server" const supabase = createAdminClient() ``` -------------------------------- ### Create Chat Room Server Action - TypeScript Source: https://context7.com/webdevsimplified/supabase-realtime-chat/llms.txt A Next.js server action to create a new chat room. It uses Zod for schema validation to ensure the provided room name and public status are valid. Upon successful validation, it creates the room in the Supabase database and automatically adds the authenticated user as a member. Finally, it redirects the user to the newly created room's page. Dependencies include Zod for validation, Supabase client for database operations, and Next.js navigation for redirects. ```typescript // src/services/supabase/actions/rooms.ts "use server" import z from "zod" import { createRoomSchema } from "../schemas/rooms" import { getCurrentUser } from "../lib/getCurrentUser" import { createAdminClient } from "../server" import { redirect } from "next/navigation" // Schema definition export const createRoomSchema = z.object({ name: z.string().min(1).trim(), isPublic: z.boolean(), }) export async function createRoom(unsafeData: z.infer) { const { success, data } = createRoomSchema.safeParse(unsafeData) if (!success) { return { error: true, message: "Invalid room data" } } const user = await getCurrentUser() if (user == null) { return { error: true, message: "User not authenticated" } } const supabase = createAdminClient() const { data: room, error: roomError } = await supabase .from("chat_room") .insert({ name: data.name, is_public: data.isPublic }) .select("id") .single() if (roomError || room == null) { return { error: true, message: "Failed to create room" } } const { error: membershipError } = await supabase .from("chat_room_member") .insert({ chat_room_id: room.id, member_id: user.id }) if (membershipError) { return { error: true, message: "Failed to add user to room" } } redirect(`/rooms/${room.id}`) } // Usage in client component "use client" import { createRoom } from "@/services/supabase/actions/rooms" import { useForm } from "react-hook-form" export function CreateRoomForm() { const form = useForm({ defaultValues: { name: "", isPublic: false } }) async function handleSubmit(data) { const result = await createRoom(data) if (result.error) { console.error(result.message) } // Redirects automatically on success } return (
) } ``` -------------------------------- ### Client Chat Input with Optimistic Updates (TypeScript) Source: https://context7.com/webdevsimplified/supabase-realtime-chat/llms.txt A client component for sending chat messages with optimistic UI updates. It handles message input, sending messages to Supabase, and managing local state for pending, successful, and failed messages. Dependencies include `lucide-react` for icons and `sonner` for toasts. ```typescript "use client" import { SendIcon } from "lucide-react" import { FormEvent, useState } from "react" import { toast } from "sonner" import { Message, sendMessage } from "@/services/supabase/actions/messages" type Props = { roomId: string onSend: (message: { id: string; text: string }) => void onSuccessfulSend: (message: Message) => void onErrorSend: (id: string) => void } export function ChatInput({ roomId, onSend, onSuccessfulSend, onErrorSend, }: Props) { const [message, setMessage] = useState("") async function handleSubmit(e?: FormEvent) { e?.preventDefault() const text = message.trim() if (!text) return setMessage("") const id = crypto.randomUUID() onSend({ id, text }) const result = await sendMessage({ id, text, roomId }) if (result.error) { toast.error(result.message) onErrorSend(id) } else { onSuccessfulSend(result.message) } } return (