This implementation uses Stream.io's built-in call duration as the primary source for tracking study sessions, providing more accurate and reliable timing data.
The system now uses Stream.io's server-side call duration tracking instead of relying solely on client-side timers. This approach provides several benefits:
- More Accurate: Server-side duration tracking eliminates client-side timing issues
- Reliable: Works even if clients disconnect unexpectedly
- Consistent: All participants get the same duration data
- Fallback Support: Falls back to client-side timing if Stream.io data is unavailable
- Route:
/api/study-sessions/stream-duration - Purpose: Handles study session start/end with Stream.io duration integration
- Features:
- Fetches call duration from Stream.io API
- Converts seconds to minutes for database storage
- Falls back to manual calculation if Stream.io data unavailable
- File:
src/lib/stream-duration-utils.ts - Functions:
getStreamCallDuration(callId): Fetches duration from Stream.ioconvertStreamDurationToMinutes(seconds): Converts to minutesgetStreamCallDetails(callId): Gets full call informationisStreamCallActive(callId): Checks if call is still activegetUserActiveCalls(userId): Gets user's active calls
- Function:
updateStudySessionWithStreamDuration(userId, callId, durationSeconds) - Purpose: Updates study sessions with Stream.io duration data
- Storage: Duration stored in minutes in the database
- File:
src/hooks/useStreamStudyTimeTracker.ts - Purpose: React hook for Stream.io duration-based tracking
- Features:
- Automatic session start/end
- Real-time duration updates
- Fallback to manual tracking if needed
- Route:
/api/webhooks/stream-call-events - Purpose: Handles Stream.io webhook events for real-time updates
- Events:
call.ended,call.started,call.member_joined,call.member_left
// Get duration from Stream.io call object
const duration = (call.call as any).duration || 0; // Duration in secondsThe StudySession model stores:
duration: Minutes (converted from Stream.io seconds)joinedAt: Session start timeleftAt: Session end timecallId: Stream.io call identifier
- Primary: Use Stream.io duration
- Fallback: Manual calculation (end time - start time)
- Error Handling: Log errors and continue with fallback
import { useStreamStudyTimeTracker } from '@/hooks/useStreamStudyTimeTracker';
const { startTracking, endTracking, isTracking, dailyHours } = useStreamStudyTimeTracker(call?.id);
// Start tracking when call is joined
useEffect(() => {
if (callingState === CallingState.JOINED && call?.id) {
startTracking();
}
}, [callingState, call?.id]);
// End tracking on unmount
useEffect(() => {
return () => {
if (isTracking) {
endTracking();
}
};
}, []);// Start session
await fetch('/api/study-sessions/stream-duration', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
userId: user.id,
callId: call.id,
action: 'start',
}),
});
// End session (uses Stream.io duration)
await fetch('/api/study-sessions/stream-duration', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
userId: user.id,
callId: call.id,
action: 'end',
}),
});NEXT_PUBLIC_STREAM_API_KEY=your_stream_api_key
STREAM_SECRET_KEY=your_stream_secret_keyTo enable webhook-based tracking:
- Configure Stream.io webhooks to point to
/api/webhooks/stream-call-events - Enable events:
call.ended,call.started,call.member_joined,call.member_left - Add webhook signature verification if needed
- Server-side duration eliminates client-side timing drift
- Handles network interruptions gracefully
- Consistent across all participants
- Works even if client disconnects unexpectedly
- No dependency on client-side JavaScript timing
- Automatic fallback to manual calculation
- Reduces client-side processing
- Leverages Stream.io's optimized duration tracking
- Minimal API calls for duration data
The system maintains backward compatibility:
- Existing Sessions: Continue to work with manual duration calculation
- New Sessions: Use Stream.io duration as primary source
- Mixed Approach: Can use both methods simultaneously
- Stream.io API calls are logged with duration data
- Fallback scenarios are logged for debugging
- Webhook events are logged for monitoring
- Graceful fallback to manual calculation
- Detailed error logging for troubleshooting
- No impact on user experience during errors
- Real-time Updates: WebSocket integration for live duration updates
- Analytics: Detailed call analytics using Stream.io data
- Multi-call Support: Handle users in multiple simultaneous calls
- Advanced Webhooks: More sophisticated webhook event handling
- Duration Validation: Cross-check Stream.io and client-side durations
-
Duration Not Available
- Check Stream.io API credentials
- Verify call exists in Stream.io
- Check webhook configuration
-
Fallback to Manual Calculation
- Normal behavior when Stream.io data unavailable
- Check logs for specific error messages
-
Webhook Not Working
- Verify webhook URL is accessible
- Check Stream.io webhook configuration
- Review webhook signature verification
// Check Stream.io call duration
const duration = await getStreamCallDuration(callId);
console.log('Call duration:', duration, 'seconds');
// Check if call is active
const isActive = await isStreamCallActive(callId);
console.log('Call active:', isActive);