ADDED SOCKET PROVIDER fixed a typo in file names

This commit is contained in:
Bob Burningham 2023-10-22 22:26:46 -07:00
parent adea34b31b
commit c517a1f354
8 changed files with 136 additions and 5 deletions

View File

@ -4,8 +4,9 @@ import { Open_Sans } from 'next/font/google'
import { ClerkProvider } from '@clerk/nextjs' import { ClerkProvider } from '@clerk/nextjs'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
import { ThemeProvider } from '@/components/providors/theme-providor' import { ThemeProvider } from '@/components/providers/theme-provider'
import { ModalProvidor } from '@/components/providors/modal-providor' import { ModalProvider } from '@/components/providers/modal-provider'
import { SocketProvider } from '@/components/providers/socket-provider'
const font = Open_Sans({ subsets: ['latin'] }) const font = Open_Sans({ subsets: ['latin'] })
@ -32,8 +33,10 @@ export default function RootLayout({
enableSystem={false} enableSystem={false}
storageKey="discord-theme" storageKey="discord-theme"
> >
<ModalProvidor/> <SocketProvider>
<ModalProvider/>
{children} {children}
</SocketProvider>
</ThemeProvider> </ThemeProvider>
</body> </body>
</html> </html>

View File

@ -1,6 +1,7 @@
import { Hash, Menu } from "lucide-react" import { Hash, Menu } from "lucide-react"
import { MobileToggle } from "@/components/mobile-toggle"; import { MobileToggle } from "@/components/mobile-toggle";
import { UserAvatar } from "../user-avatar"; import { UserAvatar } from "@/components/user-avatar";
import { SocketIndicator } from "@/components/socket-indicator";
interface ChatHeaderProps { interface ChatHeaderProps {
serverId: string; serverId: string;
@ -31,6 +32,9 @@ export const ChatHeader = ({
<p className="font-semibold text-md text-black dark:text-white"> <p className="font-semibold text-md text-black dark:text-white">
{name} {name}
</p> </p>
<div className="ml-auto flex items-center">
<SocketIndicator/>
</div>
</div> </div>
) )
} }

View File

@ -12,7 +12,7 @@ import { DeleteServerModal } from "@/components/modals/delete-server-modal";
import { DeleteChannelModal } from "@/components/modals/delete-channel-modal"; import { DeleteChannelModal } from "@/components/modals/delete-channel-modal";
import { EditChannelModal } from "@/components/modals/edit-channel-modal"; import { EditChannelModal } from "@/components/modals/edit-channel-modal";
export const ModalProvidor = () => { export const ModalProvider = () => {
const [isMounted, setIsMounted] = useState(false); const [isMounted, setIsMounted] = useState(false);
useEffect (() => { useEffect (() => {

View File

@ -0,0 +1,59 @@
"use client";
import {
createContext,
useContext,
useEffect,
useState
} from "react";
import { io as ClientIO } from "socket.io-client";
type SocketContextType = {
socket: any | null;
isConnected: boolean;
};
const SocketContext = createContext<SocketContextType>({
socket: null,
isConnected: false,
});
export const useSocket = () => {
return useContext(SocketContext);
};
export const SocketProvider = ({
children
}: {
children: React.ReactNode
}) => {
const [socket, setSocket] = useState(null);
const [isConnected, setIsConnected] = useState(false);
useEffect(() => {
const socketInstance = new (ClientIO as any)(process.env.NEXT_PUBLIC_SITE_URL!, {
path: "/api/socket/io",
addTrailingSlash: false,
});
socketInstance.on("connect", () => {
setIsConnected(true);
});
socketInstance.on("disconnect", () => {
setIsConnected(false);
});
setSocket(socketInstance);
return () => {
socketInstance.disconnect();
}
}, []);
return (
<SocketContext.Provider value={{ socket, isConnected }}>
{children}
</SocketContext.Provider>
)
}

View File

@ -0,0 +1,28 @@
"use client";
import { useSocket } from "@/components/providers/socket-provider";
import { Badge } from "@/components/ui/badge";
export const SocketIndicator = () => {
const { isConnected } = useSocket();
if (!isConnected) {
return (
<Badge
variant="outline"
className="bg-yellow-600 text-white border-none"
>
Fallback: Polling every 1s
</Badge>
)
}
return (
<Badge
variant="outline"
className="bg-emerald-600 text-white border-none"
>
Live: Real-time Updates
</Badge>
)
}

26
pages/api/socket/io.ts Normal file
View File

@ -0,0 +1,26 @@
import { Server as NetServer } from 'http';
import { NextApiRequest } from 'next';
import { Server as ServerIO } from 'socket.io';
import { NextApiResponseServerIo } from '@/types';
export const config = {
api: {
bodyParser: false,
}
}
const ioHandler = (req: NextApiRequest, res: NextApiResponseServerIo) => {
if (!res.socket.server.io) {
const path = "/api/socket/io"
const httpServer: NetServer = res.socket.server as any;
const io = new ServerIO(httpServer, {
path: path,
addTrailingSlash: false,
});
res.socket.server.io = io;
}
res.end();
}
export default ioHandler;

View File

@ -1,5 +1,16 @@
import { Server as NetServer, Socket } from 'net';
import { NextApiResponse } from 'next';
import { Server as SocketIOServer } from 'socket.io';
import { Server, Member, Profile } from '@prisma/client'; import { Server, Member, Profile } from '@prisma/client';
export type ServerWithMembersWithProfiles = Server & { export type ServerWithMembersWithProfiles = Server & {
members: (Member & { profile: Profile })[]; members: (Member & { profile: Profile })[];
} }
export type NextApiResponseServerIo = NextApiResponse & {
socket: Socket & {
server: NetServer & {
io: SocketIOServer;
}
}
}