aboutsummaryrefslogtreecommitdiffstats
path: root/app/src/hooks
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/hooks')
-rw-r--r--app/src/hooks/useSocket.ts104
-rw-r--r--app/src/hooks/useSocket.tsx127
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