Upload source code

This commit is contained in:
2026-03-12 19:36:34 +00:00
parent 783b6cb7e8
commit c7fb0c8561
158 changed files with 22553 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
import { useContext } from 'react';
import { NotificationContext } from '../contexts/NotificationContextState';
export const useNotifications = () => {
const context = useContext(NotificationContext);
if (!context) {
throw new Error('useNotifications must be used within a NotificationProvider');
}
return context;
};

View File

@@ -0,0 +1,58 @@
import { useState, useEffect, useCallback } from 'react';
import api from '../services/api';
import { useAuth } from '../context/useAuth';
export interface ActiveUser {
_id: string;
name: string;
email: string;
clerkId: string;
lastSeenAt: string;
}
export const usePresence = () => {
const { isSignedIn, appUser, isLoading } = useAuth();
const [activeUsers, setActiveUsers] = useState<ActiveUser[]>([]);
const sendHeartbeat = useCallback(async () => {
// Only send heartbeat if user is signed in, not loading, and appUser exists
if (!isSignedIn || isLoading || !appUser) return;
try {
await api.post('/users/heartbeat');
} catch (error) {
console.error('Failed to send heartbeat', error);
}
}, [isSignedIn, isLoading, appUser]);
const fetchActiveUsers = useCallback(async () => {
// Only fetch if user is signed in, not loading, and appUser exists
if (!isSignedIn || isLoading || !appUser) return;
try {
const response = await api.get<ActiveUser[]>('/users/active');
setActiveUsers(response.data);
} catch (error) {
console.error('Failed to fetch active users', error);
}
}, [isSignedIn, isLoading, appUser]);
useEffect(() => {
// Wait until user is signed in, not loading, and appUser exists
if (!isSignedIn || isLoading || !appUser) return;
// Initial call
sendHeartbeat();
fetchActiveUsers();
// Interval
const interval = setInterval(() => {
sendHeartbeat();
fetchActiveUsers();
}, 60000); // 1 minute
return () => clearInterval(interval);
}, [isSignedIn, isLoading, appUser, sendHeartbeat, fetchActiveUsers]);
return { activeUsers };
};

View File

@@ -0,0 +1,2 @@
// Re-export from the new context file to maintain backward compatibility
export { useToast } from '../context/ToastContext';

View File

@@ -0,0 +1,45 @@
import { useState, useEffect } from 'react';
import api from '../services/api';
import { useAuth } from '../context/useAuth';
export const useUnreadMessages = () => {
const { isSignedIn, appUser } = useAuth();
const [unreadCount, setUnreadCount] = useState(0);
const [hasUnread, setHasUnread] = useState(false);
useEffect(() => {
if (!isSignedIn || !appUser) return;
const fetchUnreadCount = async () => {
try {
const response = await api.get('/messages/unread');
const count = response.data.length;
setUnreadCount(count);
setHasUnread(count > 0);
} catch (error) {
console.error('Error fetching unread messages:', error);
}
};
// Initial fetch
fetchUnreadCount();
// Poll every 30 seconds
const interval = setInterval(fetchUnreadCount, 30000);
return () => clearInterval(interval);
}, [isSignedIn, appUser]);
const refreshUnreadCount = async () => {
try {
const response = await api.get('/messages/unread');
const count = response.data.length;
setUnreadCount(count);
setHasUnread(count > 0);
} catch (error) {
console.error('Error fetching unread messages:', error);
}
};
return { unreadCount, hasUnread, refreshUnreadCount };
};