diff options
Diffstat (limited to 'app/src/hooks')
-rw-r--r-- | app/src/hooks/useSocket.ts | 104 | ||||
-rw-r--r-- | app/src/hooks/useSocket.tsx | 127 |
2 files changed, 231 insertions, 0 deletions
diff --git a/app/src/hooks/useSocket.ts b/app/src/hooks/useSocket.ts new file mode 100644 index 0000000..8e2e695 --- /dev/null +++ b/app/src/hooks/useSocket.ts @@ -0,0 +1,104 @@ +"use client"; + +import { useEffect, useRef, useState } from 'react'; +import { io, Socket } from 'socket.io-client'; + +export const useSocket = () => { + const [isConnected, setIsConnected] = useState(false); + const socketRef = useRef<Socket | null>(null); + + useEffect(() => { + // Initialize socket connection only on client + if (typeof window === 'undefined') return; + + // Create a socket connection + const socket = io({ + path: '/api/socket', + addTrailingSlash: false, + }); + + // Set up event handlers + socket.on('connect', () => { + console.log('Socket connected:', socket.id); + setIsConnected(true); + }); + + socket.on('disconnect', () => { + console.log('Socket disconnected'); + setIsConnected(false); + }); + + socket.on('connect_error', (err) => { + console.error('Socket connection error:', err); + setIsConnected(false); + }); + + // Store socket reference + socketRef.current = socket; + + // Clean up on unmount + return () => { + socket.disconnect(); + }; + }, []); + + // Join a location share room + const joinLocationShare = (shareToken: string) => { + if (socketRef.current && shareToken) { + socketRef.current.emit('share:join', shareToken); + } + }; + + // Leave a location share room + const leaveLocationShare = (shareToken: string) => { + if (socketRef.current && shareToken) { + socketRef.current.emit('share:leave', shareToken); + } + }; + + // Send a location update + const sendLocationUpdate = (data: { + latitude: number; + longitude: number; + accuracy?: number; + shareToken?: string; + }) => { + if (socketRef.current) { + socketRef.current.emit('location:update', { + ...data, + timestamp: new Date().toISOString(), + }); + } + }; + + // Subscribe to location updates for a specific share + const subscribeToLocationUpdates = ( + shareToken: string, + callback: (data: any) => void + ) => { + if (!socketRef.current) return () => {}; + + // Join the location share room + joinLocationShare(shareToken); + + // Listen for location updates + socketRef.current.on('location:updated', callback); + + // Return cleanup function + return () => { + if (socketRef.current) { + socketRef.current.off('location:updated', callback); + leaveLocationShare(shareToken); + } + }; + }; + + return { + socket: socketRef.current, + isConnected, + joinLocationShare, + leaveLocationShare, + sendLocationUpdate, + subscribeToLocationUpdates, + }; +};
\ No newline at end of file diff --git a/app/src/hooks/useSocket.tsx b/app/src/hooks/useSocket.tsx new file mode 100644 index 0000000..ee710b8 --- /dev/null +++ b/app/src/hooks/useSocket.tsx @@ -0,0 +1,127 @@ +"use client"; + +import { useState, useEffect, useCallback, createContext, useContext, ReactNode } from "react"; +import io, { Socket } from "socket.io-client"; +import { toast } from "sonner"; + +interface LocationType { + latitude: number; + longitude: number; + accuracy: number; + timestamp: number; +} + +interface SocketContextType { + socket: Socket | null; + isConnected: boolean; + location: LocationType | null; + startSharing: (token: string) => void; + stopSharing: () => void; + joinLocationView: (token: string) => void; + updateLocation: (location: LocationType) => void; +} + +const SocketContext = createContext<SocketContextType>({ + socket: null, + isConnected: false, + location: null, + startSharing: () => {}, + stopSharing: () => {}, + joinLocationView: () => {}, + updateLocation: () => {}, +}); + +export const SocketProvider = ({ children }: { children: ReactNode }) => { + const [socket, setSocket] = useState<Socket | null>(null); + const [isConnected, setIsConnected] = useState(false); + const [location, setLocation] = useState<LocationType | null>(null); + + useEffect(() => { + // Initialize socket connection + const socketInstance = io(process.env.NEXT_PUBLIC_SOCKET_URL || "http://localhost:3001", { + transports: ["websocket"], + autoConnect: true, + }); + + socketInstance.on("connect", () => { + setIsConnected(true); + console.log("Connected to socket server"); + }); + + socketInstance.on("disconnect", () => { + setIsConnected(false); + console.log("Disconnected from socket server"); + }); + + socketInstance.on("location-update", (data: LocationType) => { + setLocation(data); + }); + + socketInstance.on("error", (error: string) => { + toast.error(error); + }); + + setSocket(socketInstance); + + return () => { + socketInstance.disconnect(); + }; + }, []); + + const startSharing = useCallback((token: string) => { + if (socket && isConnected) { + socket.emit("start-sharing", { token }); + toast.success("Started sharing your location"); + } else { + toast.error("Socket connection not available"); + } + }, [socket, isConnected]); + + const stopSharing = useCallback(() => { + if (socket && isConnected) { + socket.emit("stop-sharing"); + toast.success("Stopped sharing your location"); + } + }, [socket, isConnected]); + + const joinLocationView = useCallback((token: string) => { + if (socket && isConnected) { + socket.emit("join-location", { token }); + } else { + // If socket isn't connected yet, we'll attempt to connect when it becomes available + const checkInterval = setInterval(() => { + if (socket && isConnected) { + socket.emit("join-location", { token }); + clearInterval(checkInterval); + } + }, 1000); + + // Clean up interval after 10 seconds if still not connected + setTimeout(() => clearInterval(checkInterval), 10000); + } + }, [socket, isConnected]); + + const updateLocation = useCallback((locationData: LocationType) => { + if (socket && isConnected) { + socket.emit("update-location", locationData); + } + }, [socket, isConnected]); + + return ( + <SocketContext.Provider + value={{ + socket, + isConnected, + location, + startSharing, + stopSharing, + joinLocationView, + updateLocation, + }} + > + {children} + </SocketContext.Provider> + ); +}; + +export const useSocket = () => useContext(SocketContext);
\ No newline at end of file |